﻿using System;
using System.Collections.Generic;
using System.Linq;
using Internal.KuiHuaBaoDian.Services.Lockstep;
using Internal.Runtime.Services.Battle;
using Internal.Runtime.Services.Battle.Entity;
using KuiHuaBaoDian.Services;
using KuiHuaBaoDian.Services.Lockstep;
using Runtime.Models;
using Runtime.Services.Audio;
using Runtime.Services.Battle.Entity;
using Runtime.Services.BattleAttack;
using Runtime.Services.BattleSkill;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Pool;
using UnityEngine.Tilemaps;

namespace Runtime.Services.Battle {

    /// <summary>
    /// 战斗服务
    /// </summary>
    public partial interface IDFR_BattleServiceBase {

        /// <summary>
        /// 战斗配置
        /// </summary>
        public interface ISetting {

            /// <summary>
            /// 相机
            /// </summary>
            Camera Camera { get; }

            /// <summary>
            /// 战斗对象根
            /// </summary>
            Transform RootTransform { get; }

            /// <summary>
            /// 锚点
            /// </summary>
            Transform AncherTransform { get; }

            /// <summary>
            /// 模型对象根
            /// </summary>
            Transform EntityRootTransform { get; }

            /// <summary>
            /// 地形地图
            /// </summary>
            Tilemap TilemapTerrain { get; }

            /// <summary>
            /// 地貌地图
            /// </summary>
            Tilemap TilemapLandform { get; }

            /// <summary>
            /// 地图格子
            /// </summary>
            Grid Grid { get; }
        }

        /// <summary>
        /// 自己的玩家ID
        /// </summary>
        DFR_BattlePlayerID MyPlayerID { get; }

        /// <summary>
        /// 配置
        /// </summary>
        ISetting Setting { get; }

        /// <summary>
        /// 单元格宽
        /// </summary>
        float CellWidth { get; }

        /// <summary>
        /// 单元格高
        /// </summary>
        float CellHeight { get; }

        /// <summary>
        /// 水平单元格数量
        /// </summary>
        uint HorizontalCellCount { get; }

        /// <summary>
        /// 垂直单元格数据
        /// </summary>
        uint VerticalCellCount { get; }

        /// <summary>
        /// 刷新所有实体的时间缩放
        /// TODO: 这个函数在这里并不是很好，主要是 Data.Changed内部的Delegate频繁Add/Remove性能不好
        /// </summary>
        void RefreshAllEntityTimeScale();

        /// <summary>
        /// 取得一个 0 至 1 之间的随机数，包括 0 ，不包括 1
        /// </summary>
        F64 Random();
        /// <summary>
        /// 取得一个 0 至 <paramref name="max"/> 的随机数，包括 0 ，不包括 <paramref name="max"/>
        /// </summary>
        /// <param name="max">最大值</param>
        F64 Random(F64 max);
        /// <summary>
        /// 取得一个 <paramref name="min"/> 至 <paramref name="max"/> 的随机数，包括 <paramref name="min"/> ，不包括 <paramref name="max"/>
        /// </summary>
        /// <param name="min">最小值</param>
        /// <param name="max">最大值</param>
        F64 Random(F64 min, F64 max);

        /// <summary>
        /// 取得位置
        /// </summary>
        /// <param name="mapPosition">地图位置</param>
        Vector3 GetLocalPosition(DFR_BattleMapPosition mapPosition);
        /// <summary>
        /// 取得位置
        /// </summary>
        /// <param name="vector3">位置</param>
        Vector3 GetLocalPosition(Vector3 vector3);
        /// <summary>
        /// 取得位置
        /// </summary>
        /// <param name="entityInstanceID">实例ID</param>
        Vector3 GetLocalPosition(DFR_BattleEntityInstanceID entityInstanceID);

        /// <summary>
        /// 取得世界位置
        /// </summary>
        /// <param name="mapPosition">地图位置</param>
        Vector3 GetWorldPosition(DFR_BattleMapPosition mapPosition);
        /// <summary>
        /// 取得世界位置
        /// </summary>
        /// <param name="entityInstanceID">实例ID</param>
        Vector3 GetWorldPosition(DFR_BattleEntityInstanceID entityInstanceID);

        /// <summary>
        /// 复制实体
        /// </summary>
        /// <param name="axis">所在单元格坐标</param>
        /// <param name="transform">变换器</param>
        /// <param name="sortingLayerID">排序层ID</param>
        [Obsolete("复制实体操作在未加载完成时有出错风险")]
        GameObject CopyEntity(DFR_BattleCellAxis axis, Transform transform, int sortingLayerID);

        /// <summary>
        /// 取得偏移量
        /// </summary>
        /// <param name="offset">偏移值</param>
        Vector3 GetRenderOffsetVector(Vector2F64 offset);

        /// <summary>
        /// 添加对象
        /// </summary>
        /// <param name="assetKey">资产键</param>
        /// <param name="mapPosition">地图位置</param>
        /// <param name="autoKillSeconds">自动销毁时间(秒)</param>
        DFR_BattleEntityHandle AttachObject(string assetKey, DFR_BattleMapPosition mapPosition, F64 autoKillSeconds = default);

        /// <summary>
        /// 添加效果维持者
        /// </summary>
        /// <param name="effectHolder">效果维持者</param>
        /// <param name="mapPosition">地图位置</param>
        /// <param name="autoKillSeconds">自动销毁时间(秒)</param>
        DFR_BattleEntityHandle AttachHolder(IDFR_BattleEffectHolder effectHolder, DFR_BattleMapPosition mapPosition, F64 autoKillSeconds = default);

        /// <summary>
        /// 添加子弹
        /// </summary>
        /// <param name="axis">单元格坐标</param>
        /// <param name="endMapPositionProvider">目标地图位置提供者</param>
        /// <param name="endLocalPositionProvider">目标位置提供者</param>
        /// <param name="attackSnapshot">攻击时的快照</param>
        /// <param name="targetEntityInstanceID">目标实例ID</param>
        /// <param name="attackBulletProvider">攻击的子弹提供者</param>
        DFR_BattleEntityHandle AttachBullet(DFR_BattleCellAxis axis,
                                                   IDFR_BattleMapPositionProvider endMapPositionProvider,
                                                   IDFR_BattleLocalPositionProvider endLocalPositionProvider,
                                                   IDFR_BattleAttackSnapshot attackSnapshot,
                                                   DFR_BattleEntityInstanceID targetEntityInstanceID,
                                                   IDFR_BattleAttackBulletProvider attackBulletProvider);

        /// <summary>
        /// 添加效果
        /// </summary>
        /// <param name="effectModel">效果</param>
        /// <param name="skillModel">技能</param>
        /// <param name="attackLookout">观察者</param>
        /// <param name="mapPosition">地图位置</param>
        /// <param name="duration">持续时间</param>
        DFR_BattleEntityHandle AttachEffect(
            IDFR_BattleEffectModelBase effectModel,
            IDFR_BattleSkillModelBase skillModel,
            IDFR_BattleAttackLookout attackLookout,
            DFR_BattleMapPosition mapPosition,
            F64 duration);

        /// <summary>
        /// 移除对象
        /// </summary>
        /// <param name="handle">实例句柄</param>
        void DetachObject(DFR_BattleEntityHandle handle);

        /// <summary>
        /// 移除维持者
        /// </summary>
        /// <param name="handle">实例句柄</param>
        void DetachHolder(DFR_BattleEntityHandle handle);

        /// <summary>
        /// 移除子弹
        /// </summary>
        /// <param name="handle">实例句柄</param>
        void DetachBullet(DFR_BattleEntityHandle handle);

        /// <summary>
        /// 移除效果
        /// </summary>
        /// <param name="handle">实例句柄</param>
        void DetachEffect(DFR_BattleEntityHandle handle);
    }

    /// <summary>
    /// 战斗服务
    /// </summary>
    public static class BattleServiceBase {

        /// <summary>
        /// 单例
        /// </summary>
        public static IDFR_BattleServiceBase Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IDFR_BattleServiceBase>();
        private static IDFR_BattleServiceBase m_Instance;

        /// <summary>
        /// 【战斗】子弹命中时为表现更好的命中特效效果，延迟扣血的时间
        /// </summary>
        public static readonly float BATTLE_BULLET_HIT_TIME = 0.05f;
    }

    public abstract partial class DFR_BattleServiceBase<TRecord> : IDFR_BattleServiceBase, IService, IDFR_BattleServiceBaseInternal where TRecord : DFR_BattleEntityRecordBase {

        private readonly ObjectPool<TRecord> m_RecordPool = new(() => Activator.CreateInstance<TRecord>(),
            actionOnGet: item => item.InternalAllocate(), actionOnRelease: item => item.InternalRelease(), collectionCheck: false);

        protected sealed class EntityCreationOption {
            public DFR_BattlePlayerID PlayerID;
            public DFR_BattleEntityInstanceID EntityInstanceID;
            public DFR_BattleCellAxis Axis;
        }

        void IService.OnApplicationQuit() => End();

        public IDFR_BattleServiceBase.ISetting Setting { get; private set; }

        public float CellWidth { get; private set; }

        public float CellHeight { get; private set; }

        public DFR_BattlePlayerID MyPlayerID { get; private set; }

        public abstract uint HorizontalCellCount { get; }

        public abstract uint VerticalCellCount { get; }

        private readonly Tilemap[] m_Tilemaps = new Tilemap[2];

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:命名样式", Justification = "<挂起>")]
        protected RandomF64 m_Random => m_RandomInstance;

        private RandomF64 m_RandomInstance;

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:命名样式", Justification = "<挂起>")]
        private Transform m_EntityRootTransform => Setting.EntityRootTransform;

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:命名样式", Justification = "<挂起>")]
        protected Tilemap m_TerrainTilemap => Setting.TilemapTerrain;

        protected IReadOnlyDictionary<DFR_BattleEntityInstanceID, TRecord> EntityRecordDictionary => m_EntityRecordDictionary;
        private Dictionary<DFR_BattleEntityInstanceID, TRecord> m_EntityRecordDictionary;
        private HashSet<TRecord> m_DetachingSet;

        private IDFR_BattleCellDictionaryModelBase m_CellModelDictionary;
        private IDFR_BattleAttackSourceDictionaryModelBase m_AttackSourceModelDictionary;
        private IDFR_BattleAttackTargetDictionaryModelBase m_AttackTargetModelDictionary;

        protected abstract IDFR_BattleMapPositionProvider GetMapPositionProvider(DFR_BattleEntityInstanceID entityInstanceID);

        protected abstract DFR_BattleMapPosition GetMapPosition(DFR_BattleEntityInstanceID entityInstanceID);

        protected void Initialize(IDFR_BattleServiceBase.ISetting setting) {
            UnityEngine.Assertions.Assert.IsNotNull(setting);

            Setting = setting;
            m_Tilemaps[0] = m_TerrainTilemap;
            //m_Tilemaps[1] = m_LandformTilemap;
        }

        protected virtual void Prepare(DFR_BattlePlayerID myPlayerID) {
            CellWidth = Setting.Grid.cellSize.x;
            CellHeight = Setting.Grid.cellSize.y;

            MyPlayerID = myPlayerID;

            m_EntityRecordDictionary = new Dictionary<DFR_BattleEntityInstanceID, TRecord>();
            m_DetachingSet = new HashSet<TRecord>();

            m_CellModelDictionary = DFR_BattleBase.DataCenter.Models.CellDictionary.AsReadWrite();
            m_AttackSourceModelDictionary = DFR_BattleBase.DataCenter.Models.AttackSourceDictionary.AsReadWrite();
            m_AttackTargetModelDictionary = DFR_BattleBase.DataCenter.Models.AttackTargetDictionary.AsReadWrite();
        }

        void IDFR_BattleServiceBaseInternal.End() => End();
        protected virtual void End() {
            foreach (var key in m_EntityRecordDictionary.Keys.ToHashSet()) {
                var entityRecord = m_EntityRecordDictionary[key];
                DetachEntity(entityRecord.Handle, isForce: true);
                m_RecordPool.Release(entityRecord);
            }
            m_EntityRecordDictionary = null;
            foreach (var record in m_DetachingSet.Where(item => !item.IsReleased)) {
                m_RecordPool.Release(record);
            }
            m_DetachingSet = null;

            foreach (var kv in m_AttackSourceModelDictionary) {
                (kv.Value as IDisposable).Dispose();
            }
            m_AttackSourceModelDictionary = null;
            foreach (var kv in m_AttackTargetModelDictionary) {
                (kv.Value as IDisposable).Dispose();
            }
            m_AttackTargetModelDictionary = null;
            foreach (var kv in m_CellModelDictionary) {
                (kv.Value as IDisposable).Dispose();
            }
            m_CellModelDictionary = null;

            m_RandomInstance = null;

            foreach (var tilemap in m_Tilemaps) {
#pragma warning disable IDE0031 // 使用 null 传播
                if (tilemap != null) {
                    tilemap.ClearAllTiles();
                }
#pragma warning restore IDE0031 // 使用 null 传播
            }

            DisposeModel();

            DFR_BattleEntityRecordBase.Clear();
            DFR_BattleEntityInstanceID.COUNT = 1;
        }

        protected virtual void DisposeModel() {
            (DFR_BattleBase.DataCenter as IDisposable).Dispose();
        }

        public void RefreshAllEntityTimeScale() {
            foreach (var kv in m_EntityRecordDictionary) {
                var record = kv.Value;
                record.RefreshTimeScale();
            }
        }

        public F64 Random() => m_RandomInstance.Next(F64.Zero, F64.One);
        public F64 Random(F64 max) => m_RandomInstance.Next(max);
        public F64 Random(F64 min, F64 max) => m_RandomInstance.Next(min, max);
        protected void SetRandomSeed(ulong seed) => m_RandomInstance = new RandomF64(seed);

        public Vector3 GetWorldPosition(DFR_BattleMapPosition mapPosition) {
            var localPosition = GetLocalPosition(mapPosition);
            return m_TerrainTilemap.LocalToWorld(localPosition);
        }
        public Vector3 GetWorldPosition(DFR_BattleEntityInstanceID entityInstanceID) {
            var localPosition = GetLocalPosition(entityInstanceID);
            return m_TerrainTilemap.LocalToWorld(localPosition);
        }

        public Vector3 GetLocalPosition(DFR_BattleMapPosition mapPosition) {
            var localPosition = m_TerrainTilemap.CellToLocalInterpolated(new Vector2(mapPosition.X.Float, mapPosition.Y.Float));
            return localPosition += new Vector3(CellWidth / 2f, CellHeight / 2f, 0f);
        }

        public Vector3 GetLocalPosition(Vector3 vector3) {
            return new Vector3(CellWidth / 2f + vector3.x * CellWidth, CellHeight / 2f + vector3.y * CellHeight, vector3.z);
        }
        public Vector3 GetLocalPosition(DFR_BattleEntityInstanceID entityInstanceID) {
            var mapPositionProvider = GetMapPositionProvider(entityInstanceID);
            if (mapPositionProvider != null) {
                return GetLocalPosition(mapPositionProvider.MapPosition);
            } else {
                var mapPosition = GetMapPosition(entityInstanceID);
                return GetLocalPosition(mapPosition);
            }
        }

        [Obsolete("复制实体操作在未加载完成时有出错风险")]
        public GameObject CopyEntity(DFR_BattleCellAxis axis, Transform transform, int sortingLayerID) {
            var index = axis.ToIndex();
            if (DFR_BattleBase.DataCenter.Models.CellDictionary.TryGetValue(index, out var result) && result is IDFR_BattleCellModelBase cellModel
                && cellModel.HasContent() && m_EntityRecordDictionary.TryGetValue(cellModel.ContentEntityInstanceID.Value, out var record)) {

                var gameObject = UnityEngine.Object.Instantiate(record.Container); // TODO: 复制实体操作在未加载完成时有出错风险
                gameObject.transform.SetParent(transform);
                gameObject.transform.ResetLocalMatrix();
                var animator = gameObject.GetComponentInChildren<Animator>(true);
                if (animator != null) {
                    animator.speed = 0;
                }
                var spriteRenderers = gameObject.GetComponentsInChildren<SpriteRenderer>();
                foreach (var renderer in spriteRenderers) {
                    renderer.color = new Color(1, 1, 1, 0.66f);
                    renderer.sortingLayerID = sortingLayerID;
                }
                var particleRenderers = gameObject.GetComponentsInChildren<ParticleSystemRenderer>();
                foreach (var renderer in particleRenderers) {
                    renderer.renderMode = ParticleSystemRenderMode.None;
                }
                var audioTriggers = gameObject.GetComponentsInChildren<DFR_AudioTrigger>();
                foreach (var audioTrigger in audioTriggers) {
                    audioTrigger.gameObject.SetActive(false);
                }
                return gameObject;
            }
            return null;
        }

        public Vector3 GetRenderOffsetVector(Vector2F64 offset) {
            var offsetX = offset.X.Float * CellWidth;
            var offsetY = offset.Y.Float * CellHeight;
            return new Vector3(offsetX, offsetY);
        }

        public abstract DFR_BattleEntityHandle AttachObject(string assetKey, DFR_BattleMapPosition mapPosition, F64 autoKillSeconds = default);

        public abstract DFR_BattleEntityHandle AttachHolder(IDFR_BattleEffectHolder effectHolder, DFR_BattleMapPosition mapPosition, F64 autoKillSeconds = default);

        public abstract DFR_BattleEntityHandle AttachBullet(DFR_BattleCellAxis axis,
                                                   IDFR_BattleMapPositionProvider endMapPositionProvider,
                                                   IDFR_BattleLocalPositionProvider endLocalPositionProvider,
                                                   IDFR_BattleAttackSnapshot attackSnapshot,
                                                   DFR_BattleEntityInstanceID targetEntityInstanceID,
                                                   IDFR_BattleAttackBulletProvider attackBulletProvider);

        public abstract DFR_BattleEntityHandle AttachEffect(IDFR_BattleEffectModelBase effectModel,
                                                   IDFR_BattleSkillModelBase skillModel,
                                                   IDFR_BattleAttackLookout attackLookout,
                                                   DFR_BattleMapPosition mapPosition,
                                                   F64 duration);

        public void DetachObject(DFR_BattleEntityHandle handle) {
            DetachEntity(handle, isForce: true);
        }
        public void DetachHolder(DFR_BattleEntityHandle handle) {
            DetachEntity(handle, isForce: true);
        }
        public void DetachEffect(DFR_BattleEntityHandle handle) {
            DetachEntity(handle, isForce: true);
        }
        public void DetachBullet(DFR_BattleEntityHandle handle) {
            DetachEntity(handle, isForce: true);
        }

        protected TRecord AttachEntity<T, TStateVehicle, TSFXVehicle>(DFR_BattlePlayerID playerID, int tableID,
                                                       DFR_BattleEntityType entityType, DFR_BattleMapPosition mapPosition,
                                                       Action<EntityCreationOption> onAttached = null, bool ignoreLockstep = false, DFR_BattleEntityInstanceID entityInstanceID = default, float localZ = 0, string entityDebugName = null, bool ignoreLockstepAssertionException = false)
            where T : IDFR_BattleEntityBase
            where TStateVehicle : Component, IDFR_BattleEntityAssetVehicle
            where TSFXVehicle : Component, IDFR_BattleEntityAssetVehicle {

            if (entityInstanceID == default) {
                entityInstanceID = new DFR_BattleEntityInstanceID { ID = new DFR_BattleEntityID { TableID = tableID, Type = entityType } };
            }
            var record = m_RecordPool.Get();
            var entity = KHBD.TypeMap.Instantiate<T>();
            var @internal = (IDFR_BattleEntityBaseInternal)entity;
            var axis = new DFR_BattleCellAxis { X = mapPosition.X.Int, Y = mapPosition.Y.Int };
            @internal.Attached += () => onAttached?.Invoke(new EntityCreationOption {
                PlayerID = playerID,
                EntityInstanceID = entityInstanceID,
                Axis = axis,
            });
            (record as IDFR_BattleEntityRecordBaseInternal).Initialize(entity);
            m_EntityRecordDictionary.Add(entityInstanceID, record);
            var lockstepEntityHandle = LockstepEntityHandle.None;
            try {
                lockstepEntityHandle = ignoreLockstep ? (LockstepService.Instance as ILockstepServiceInternal).AttachEntity(entity) : LockstepService.Instance.AttachEntity(entity);
            } catch (AssertionException) {
                m_EntityRecordDictionary.Remove(entityInstanceID);
                m_RecordPool.Release(record);
                if (!ignoreLockstepAssertionException) {
                    throw;
                }
                return null;
            }
            var handle = new DFR_BattleEntityHandle {
                EntityInstanceID = entityInstanceID,
                LockstepEntityHandle = lockstepEntityHandle,
            };
            var worldPosition = m_TerrainTilemap.CellToLocalInterpolated(new Vector2(mapPosition.X.Float, mapPosition.Y.Float));
            record.Placehold<TStateVehicle, TSFXVehicle>(handle, m_EntityRootTransform, worldPosition, localZ, entityDebugName);
            @internal.Initialize(handle, record.Container, record.Transformer.transform, record.ModelContainer.transform, record.StateVehicle, record.SFXVehicle);
            record.Done(handle);
            return record;
        }

        protected void DetachEntity(DFR_BattleEntityHandle handle, Action onDetached = null, bool isForce = false) {
            // 有些特殊情况，比如Editor停止播放时，其它异步后置完成时会调用销毁实体，这个时候服务其实也已经销毁了，需要空引用判断
            if (m_EntityRecordDictionary != null && m_EntityRecordDictionary.TryGetValue(handle.EntityInstanceID, out var record)) {
                m_EntityRecordDictionary.Remove(handle.EntityInstanceID);
                // 如果是在战斗结束时(非End函数)有调用移除，帧同步服务可能已经停止或即将停止运作
                // 所以需要加入一个移除集合中，在战斗结束调用End时就会将集合中的Record释放
                m_DetachingSet.Add(record);
                var @internal = (IDFR_BattleEntityBaseInternal)record.Entity;
                @internal.Detached += () => {
                    m_DetachingSet.Remove(record);
                    m_RecordPool.Release(record);
                    onDetached?.Invoke();
                };
                if (isForce) {
                    (LockstepService.Instance as ILockstepServiceInternal).DetachEntity(record.Handle.LockstepEntityHandle);
                } else {
                    LockstepService.Instance.DetachEntity(record.Handle.LockstepEntityHandle);
                }
            }
        }
    }
}

namespace Internal.Runtime.Services.Battle {

    public interface IDFR_BattleServiceBaseInternal {

        void End();
    }
}